﻿/*

  The contents of this file are subject to the Mozilla Public License Version
  1.1 (the "License"); you may not use this file except in compliance with
  the License. You may obtain a copy of the License at 
  
           http://www.mozilla.org/MPL/ 
  
  Software distributed under the License is distributed on an "AS IS" basis,
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  for the specific language governing rights and limitations under the License. 
  
  The Original Code is AST'r Framework.
  
  The Initial Developer of the Original Code is
  ALCARAZ Marc (aka eKameleon) <vegas@ekameleon.net>.
  Portions created by the Initial Developer are Copyright (C) 2004-2007
  the Initial Developer. All Rights Reserved.
  
  Contributor(s) :
  
*/

import flash.geom.Rectangle;

import andromeda.model.ModelCollector;
import andromeda.util.visitor.IVisitable;
import andromeda.util.visitor.IVisitor;

import asgard.display.BackgroundDisplay;
import asgard.media.SoundLibrary;

import ewhiteboard.display.whiteboard.shapes.WhiteBoardShapeDisplay;
import ewhiteboard.events.ShapeEvent;
import ewhiteboard.model.ModelList;
import ewhiteboard.model.WhiteBoardPluginCollector;
import ewhiteboard.plugins.whiteboard.IWhiteBoardPlugin;
import ewhiteboard.vo.ShapeVO;

import lunas.display.container.SimpleContainerDisplay;

import pegas.draw.RectanglePen;
import pegas.events.ButtonEvent;
import pegas.geom.Dimension;

import vegas.data.map.ArrayMap;
import vegas.events.Delegate;
import vegas.events.EventListener;

/**
 * This display defines a whiteboard. 
 * @author eKameleon
 */
class ewhiteboard.display.whiteboard.WhiteBoardDisplay extends BackgroundDisplay implements IVisitable
{

	// TODO Creates bounding box and transformation tools (move/resize)
	// TODO don't use IVisitor with the tools ? Use only controllers ? 
		
	/**
	 * Creates a new WhiteBoardDisplay instance.
	 * @param sName the name of the display.
	 * @param target the DisplayObject instance control this target.
	 */
	public function WhiteBoardDisplay( sName:String, target , bGlobal:Boolean , sChannel:String ) 
	{
		
		super( sName, target, bGlobal, sChannel );

		_listenerClick = new Delegate(this, _select) ;
		_listenerOver  = new Delegate(this , _over ) ;

		_map = new ArrayMap() ;

		// views

		canvas    = view.createEmptyMovieClip( DEFAULT_CANVAS_NAME , DEFAULT_CANVAS_DEPTH ) ;
		container = new SimpleContainerDisplay( null , createChild( null,  DEFAULT_CONTAINER_NAME , DEFAULT_CONTAINER_DEPTH ) ) ;
		layer     = view.createEmptyMovieClip( DEFAULT_LAYER_NAME  , DEFAULT_LAYER_DEPTH   ) ;
		
		// behaviours
		
		background.useHandCursor = false ;
		background.onPress = function()
		{
			// doNothing	
		} ;
		
		canvas.useHandCursor = false ;
		canvas.onPress   = Delegate.create( this, _canvasPress ) ; // only when selector tool is active.
		canvas.onRelease = canvas.onReleaseOutside = Delegate.create(this, _canvasRelease) ;
		
		_penCanvas = new RectanglePen( canvas ) ;
		
		setup() ;
	}

	/**
	 * The default event name when the canvas is press.
	 */
	public static var CANVAS_PRESS:String = "canvasPress" ;
	
	/**
	 * The default event name when the canvas is release.
	 */
	public static var CANVAS_RELEASE:String = "canvasRelease" ;

	/**
	 * The default event name when a new shape is created.
	 */
	public static var CREATE_SHAPE:String = "createShape" ;
	
	/**
	 * The default active color of this display.
	 */
	public static var DEFAULT_ACTIVE_COLOR:Number = 0x000000 ;
	
	/**
	 * The depth of the canvas MovieClip reference.
	 */
	public static var DEFAULT_CANVAS_DEPTH:Number = 1000 ;

	/**
	 * The name of the canvas MovieClip reference.
	 */
	public static var DEFAULT_CANVAS_NAME:String = "canvas" ;

	/**
	 * The depth of the container MovieClip reference.
	 */
	public static var DEFAULT_CONTAINER_DEPTH:Number = 2000 ;

	/**
	 * The name of the container MovieClip reference.
	 */
	public static var DEFAULT_CONTAINER_NAME:String = "container" ;
	
	/**
	 * The depth of the canvas MovieClip reference.
	 */
	public static var DEFAULT_LAYER_DEPTH:Number = 10000 ;

	/**
	 * The name of the canvas MovieClip reference.
	 */
	public static var DEFAULT_LAYER_NAME:String = "layer" ;

	/**
	 * Returns the active color of this display.
	 * @return the active color of this display.
	 */
	public function get activeColor():Number
	{
		return _activeColor ;
	}
	
	/**
	 * Sets the active color of this display.
	 */
	public function set activeColor( hex:Number ):Void
	{
		_activeColor = hex > 0 ? hex : DEFAULT_ACTIVE_COLOR ;
	}

	/**
	 * Returns the active thickness of this display.
	 * @return the active thickness of this display.
	 */
	public function get activeThickness():Number
	{
		return _activeThickness ;
	}
	
	/**
	 * Sets the active thickness of this display.
	 */
	public function set activeThickness( n:Number ):Void
	{
		_activeThickness = n > 0 ? n : 0 ;
	}

	/**
	 * The boundingBox area of this whiteboard.
	 */
	public var boundingBox:MovieClip ;

	/**
	 * The drawing area of this whiteboard.
	 */
	public var canvas:MovieClip ;

	/**
	 * The container of all shapes in this whiteboard.
	 */
	public var container:SimpleContainerDisplay ;

	/**
	 * Indicates if the protect background is enabled or not. 
	 */
	public function get enabledBackground():Boolean
	{
		return background.enabled ;	
	}
	
	/**
	 * @private
	 */
	public function set enabledBackground( b:Boolean ):Void 
	{
		background.enabled = b ;
	}

	/**
	 * The layer use to draw the shape when the user press the canvas area.
	 */
	public var layer:MovieClip ;

	/**
	 * The id of the sound to be invoqued when a shape display is over.
	 */
	public var soundOver:String ;

	/**
	 * The id of the sound to be invoqued when a shape display is selected.
	 */
	public var soundSelected:String ;

	/**
	 * The alpha value of the screen.
	 */
	public var themeAlpha:Number = 100 ;

	/**
	 * The alpha value of the border of this display.
	 */
	public var themeBorderAlpha:Number = null ;

	/**
	 * The color value of the border of this display.
	 */
	public var themeBorderColor:Number = null ;

	/**
	 * The thickness of the border of this display.
	 */
	public var themeBorderThickness:Number = null ;

	/**
	 * The color value of the screen.
	 */
	public var themeColor:Number = 0xCCCCCC ;

	/**
	 * Indicates the tool type value of the whiteboard.
	 */
	public function get tool():Number
	{
		return getTool() ;
	}

	/**
	 * @private
	 */
	public function set tool( n:Number ):Void
	{
		setTool( n ) ;
		update() ;
	}

	/**
	 * (read-write) Indicates if this container use a scrollRect reference to mask the content.
	 */
	public function get useScrollRect():Boolean
	{
		return _useScrollRect ;
	}

	/**
	 * @private
	 */
	public function set useScrollRect( b :Boolean ):Void
	{
		_useScrollRect = b ;
		update() ;
	}

	/**
	 * Accept an IVisitor object. 
	 * You can overrides this method in complexe Visitor pattern implementation.
	 */
	public function accept( visitor:IVisitor ) 
	{
		visitor.visit( this ) ;
	}

	/**
	 * Clear the whiteboard display.
	 */
	public function clear():Void
	{
		_map.clear() ;
		container.clear() ;
	}

	/**
	 * Returns {@true} if the specified value object exist in the whiteboard.
	 * @return {@true} if the specified value object exist in the whiteboard.
	 */
	public function contains( vo:ShapeVO ):Boolean
	{
		return _map.containsKey( vo ) ;	
	}
	
	/**
	 * Creates a new shape display with the specified ShapeVO value object.
	 */
	public function createNewShapeDisplay( vo:ShapeVO )
	{
		var clazz:Function   = getShapeDisplayRenderer() ;
		if ( clazz != null )
		{
			var target:MovieClip               = container.addChild( null ) ;
			var display:WhiteBoardShapeDisplay = new clazz( "shape_" + _cpt++ , target ) ;
			_map.put ( vo , display ) ;
			setShapeDisplay( display , vo ) ;
			update() ;
		}
		else
		{
			getLogger().warn( this + " createNewShapeDisplay failed with a 'null' or 'undefined' WhiteBoardShapeDisplay constructor.") ;	
		}
	}

	private static var _cpt:Number = 0 ;

	/**
	 * Draw the display.
	 * @return the Dimension object who defines the width and the height use in the method to draw the background.
	 */
	public function draw( w:Number , h:Number , offsetX:Number , offsetY:Number ):Dimension
	{
		var d:Dimension = super.draw( w, h, offsetX, offsetY ) ;
		
		_penCanvas.clear() ;
		_penCanvas.lineStyle( themeBorderThickness, 0 , 0 ) ;
		_penCanvas.beginFill( 0  , 0 ) ;
		_penCanvas.draw( d.width , d.height ) ;
		
		view.scrollRect = useScrollRect ? new Rectangle( offsetX, offsetY , d.width, d.height ) : null ;
		
		return d ; 	
	}
	
	/**
	 * Returns the event name use when the canvas of this whiteboard is press.
	 * @return the event name use when the canvas of this whiteboard is press.
	 */
	public function getEventTypeCANVAS_PRESS():String
	{
		return _eCanvasPress.getType() ;	
	}

	/**
	 * Returns the event name use when the canvas of this whiteboard is release.
	 * @return the event name use when the canvas of this whiteboard is release.
	 */
	public function getEventTypeCANVAS_RELEASE():String
	{
		return _eCanvasRelease.getType() ;	
	}

	/**
	 * Returns the event name use when a new ShapeVO is created.
	 * @return the event name use when a new ShapeVO is created.
	 */
	public function getEventTypeCREATE_SHAPE():String
	{
		return _eCanvasRelease.getType() ;	
	}

	/**
	 * Returns the WhiteBoardPluginCollector model reference of the application.
	 * @return the WhiteBoardPluginCollector model reference of the application.
	 */
	public function getPlugins():WhiteBoardPluginCollector
	{
		return WhiteBoardPluginCollector( ModelCollector.get( ModelList.PLUGIN_MODEL ) ) ;
	}
	
	/**
	 * Returns the whiteboard shape display renderer constructor.
	 * @return the whiteboard shape display renderer constructor.
	 */
	public function getShapeDisplayRenderer():Function
	{
		var plugins:WhiteBoardPluginCollector = getPlugins() ;
		var toolID:Number = getTool() ;
		if (plugins.containsByID( toolID ) )
		{
			var plugin:IWhiteBoardPlugin = IWhiteBoardPlugin( plugins.getVO( toolID ) ) ;
			var shapeFunction:Function   = plugin.getShape() ;
			return shapeFunction || null ;
		}
		else
		{
			return null ;	
		}
	}
	
	/**
	 * Returns the tool of the whiteboard.
	 * @return the tool of the whiteboard.
	 */
	public function getTool():Number
	{
		return _tool ;
	}
	
	/**
	 * Init the events of this display.
	 */
	public function initEvent():Void
	{
		super.initEvent() ;
		_eCanvasPress   = new ButtonEvent( CANVAS_PRESS   , this ) ;
		_eCanvasRelease = new ButtonEvent( CANVAS_RELEASE , this ) ;
		_eCreate        = new ShapeEvent ( CREATE_SHAPE   , null, this ) ;
	}
	
	/**
	 * Inserts a new ShapeVO object in the display.
	 */
	public function insert( vo:ShapeVO ):Void
	{
		if ( !contains( vo ) )
		{
			createNewShapeDisplay( vo ) ;
		}
		else
		{
			getLogger().warn( this + " add method failed : " + vo + ", this vo allready exist in the list.") ;	
		}
	}
	
	/**
	 * Notify a ShapeEvent when a new ShapeVO is created.
	 */
	public function notifyCreate( shape:ShapeVO ):Void
	{
		if ( shape != null )
		{
			_eCreate.setShape( shape ) ;
			dispatchEvent( _eCreate ) ;
		}
		else
		{
			getLogger().warn(this + " notify a create event failed with an empty 'shape' argument.") ;	
		}	
	}
	
	/**
	 * Removes the specified value object in the list.
	 */
	public function remove( vo:ShapeVO ):Void
	{
		if ( contains( vo ) )
		{
			var display:WhiteBoardShapeDisplay = _map.get( vo ) ;
			var index:Number = container.indexOf( display.view ) ;		
			if (index > -1)
			{
				_map.remove(vo) ;
				container.removeChildAt( index ) ;
				update() ;
			}
			else
			{
				getLogger().warn( this + " remove method failed, the specified ShapeVO's display don't exist in the whiteboard.") ;	
			}
		}
		else
		{
			getLogger().warn( this + " remove method failed, the specified ShapeVO isn't register in the whiteboard.") ;
		}
	}	
	
	/**
	 * Sets the event name use when the canvas of this whiteboard is press.
	 */
	public function setEventTypeCANVAS_PRESS( type:String ):Void
	{
		_eCanvasPress.setType(type) ;
	}

	/**
	 * Sets the event name use when the canvas of this whiteboard is release.
	 */
	public function setEventTypeCANVAS_RELEASE( type:String ):Void
	{
		_eCanvasRelease.setType(type) ;	
	}

	/**
	 * Sets the event name use when a new ShapeVO is created.
	 */
	public function setEventTypeCREATE_SHAPE( type:String ):Void
	{
		_eCreate.setType( type ) ;	
	}

	/**
	 * Select the specified ShapeVO in the whiteboard display.
	 */
	public function select( vo:ShapeVO ):Void
	{
		// overrides this method
	}
	

	/**
	 * Sets the specified cell with the specified value object.
	 */
	public function setShapeDisplay( display:WhiteBoardShapeDisplay, vo:ShapeVO )
	{
		display.addEventListener( ButtonEvent.OVER   , _listenerOver ) ;
		display.addEventListener( ButtonEvent.CLICK  , _listenerClick  ) ;
		display.setShape( vo ) ;	
		return display ;
	}

	/**
	 * Sets the tool of the whiteboard.
	 */
	public function setTool( id:Number ):Void
	{
		var plugins:WhiteBoardPluginCollector = getPlugins() ;
		if ( plugins.containsByID( id ) )
		{
			_tool = id ;
		}
		else
		{
			getLogger().warn(this + " set tool failed with a no register tool id : " + id ) ;	
		}
	}

	/**
	 * Invoqued when the display is refresh.
	 */
	public function viewChanged():Void
	{
		//
	}
	
	/**
	 * @private
	 */
	private var _activeColor:Number = 0x000000 ;

	/**
	 * @private
	 */
	private var _activeThickness:Number = 1 ;
	
	/**
	 * @private
	 */
	private var _eCanvasPress:ButtonEvent ;

	/**
	 * @private
	 */
	private var _eCanvasRelease:ButtonEvent ;

	/**
	 * @private
	 */
	private var _eCreate:ShapeEvent ;

	/**
	 * @private
	 */
	private var _h:Number = 270 ;

	/**
	 * @private
	 */
	private var _listenerClick:EventListener ;

	/**
	 * @private
	 */
	private var _listenerOver:EventListener ;

	/**
	 * @private
	 */
	private var _map:ArrayMap ;

	/**
	 * @private
	 */
	private var _penCanvas:RectanglePen ;

	/**
	 * @private
	 */
	private var _useScrollRect:Boolean = false ;

	/**
	 * @private
	 */
	private var _tool:Number ;
	
	/**
	 * @private
	 */
	private var _w:Number = 450 ;
	
	/**
	 * Invoqued when the canvas area is pressed.
	 */
	private function _canvasPress() : Void 
	{
		dispatchEvent( _eCanvasPress ) ;
	}
	
	/**
	 * Invoqued when the canvas area is released.
	 */
	private function _canvasRelease() : Void 
	{
		dispatchEvent( _eCanvasRelease ) ;	
	}

	/**
	 * Invoqued when a shape display is over in the container.
	 */
	private  function _over( e:ButtonEvent ):Void
	{
		if (soundOver.length > 0)
		{
			SoundLibrary(ModelCollector.get( ModelList.SOUND_MODEL ) ).getSound( soundOver ).start() ;
		}
		//var vo:ShapeVO = WhiteBoardShapeDisplay(e.getTarget()).getShape() ;
	}

	/**
	 * Invoqued when a shape display is selected in the container.
	 */
	private function _select( e:ButtonEvent ):Void
	{
		
		if (soundSelected.length > 0)
		{
			SoundLibrary(ModelCollector.get( ModelList.SOUND_MODEL) ).getSound( soundSelected ).start() ;
		}
		
		//var vo:ShapeVO = WhiteBoardShapeDisplay(e.getTarget()).getShape() ;

	}

}